﻿// Utility.cs
//
// Copyright 2008 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;

namespace YaTools.Yaml
{
    [SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors")]
    public class Utility
    {
        //// Parsing character constants
        //protected const char NUL = (char) 0;
        //protected const char TAB = (char) 0x09;
        //protected const char CR = (char) 0x0d;
        //protected const char LF = (char) 0x0a;
        //protected const char ESC = (char) 0x1b;
        //protected const char EOF = (char) 0xffff;
        //protected const char SPACE = ' ';
        //protected const char BACKSLASH = '\\';
        //protected const char SINGLE_QUOTE = '\'';
        //protected const char DOUBLE_QUOTE = '"';
        //// 
        //// YAML 1.2 no longer supports these as line breaks.  
        ////
        //// They will now be internal so the debug character formatting 
        //// support can still use them.
        ////
        //private const char NEL = (char) 0x85;
        //private const char LS = (char) 0x2028;
        //private const char PS = (char) 0x2029;

        private const char VisibleSpace = '␣';

        public static string DisplayChar(int character)
        {
            switch (character)
            {
                case Unicode.Space:
                    return "SPACE";
                case Unicode.Tab:
                    return "TAB";
                case Unicode.NUL:
                    return "NUL";
                case Unicode.EOS:
                    return "<EOF>";
                default:
                    return DebugDisplay(new string((char) character, 1));
            }
        }

        // Used to make a string more easily understood in the debugger
        public static string VisibleSpacing(string value)
        {
            StringBuilder sb = new StringBuilder(value)
                .Replace(Unicode.Space, VisibleSpace)
                .Replace(Unicode.Tab, '→')
                .Replace(Unicode.PS, '¶')
                .Replace(Unicode.LS, '⇓')
                .Replace("\r", "\r  ") // Inject spaces after line breaks
                .Replace("\n", "\n  ")
                .Replace("  \n", "\n");
            while (sb.Length > 0 && sb[sb.Length - 1] == ' ')
                sb.Length--;
            sb.Insert(0, "« ");
            sb.Append(" »");
            return sb.ToString();
        }

        public static object DebugDisplay(object value)
        {
            if (value == null) return DebugDisplay(null);

            var s = value as string;
            return (s == null) ? value : DebugDisplay(s);
        }

        public static string DebugDisplay(string value)
        {
            if (value == null) return "(null)";

            var sb = new StringBuilder();

            for (int i = 0; i < value.Length; i++)
            {
                char ch = value[i];
                switch (ch)
                {
                    case Unicode.SingleQuote:
                        sb.Append(ch);
                        break;

                    case Unicode.DoubleQuote:
                        // Special treatment so that VS 
                        // debugger doesn't backslash it.
                        sb.Append('”');
                        break;

                    case Unicode.CR:
                        sb.Append('↲');
                        break;
                    case Unicode.LF:
                        sb.Append('↓');
                        break;
                    case Unicode.LS:
                        sb.Append('⇓');
                        break;
                    case Unicode.PS:
                        sb.Append('¶');
                        break;
                    case Unicode.NEL:

                        if (ch == Unicode.CR && i + 2 < value.Length
                            && value[i + 1] == Unicode.LF)
                            i++;

                        sb.AppendLine();
                        break;

                    case Unicode.Space:
                        sb.Append(VisibleSpace); // Make spaces visible
                        break;

                    case Unicode.Tab:
                        sb.Append('→'); // Use a symbol for tabs
                        break;

                    default:
                        if (char.IsControl(ch))
                            sb.Append((char) (2400 + ch));
                        else
                            sb.Append(ch);
                        break;
                }
            }
#if ENQUOTE
            sb.Append('>');
#endif
            return sb.ToString();
        }

#if OLDCONTRACTHELPERS
        public static void ValidateArgumentRange(int value, int min, int max, string name)
        {
            if (value < min || value > max)
                throw new ArgumentOutOfRangeException(
                    name, value, "Argument out of range");
        }

        public static void ValidateArgumentNotNull(object value, string name)
        {
            if (value == null)
                throw new ArgumentNullException(name);
        }

        public static void ValidateArgument(bool assertion, string message, string name)
        {
            if (!assertion)
                throw new ArgumentException(message, name);
        }
#endif
    }

    public static class StringExtensions
    {
        private static readonly char[] SpecialBreaks = new[] {Unicode.NEL, Unicode.LS, Unicode.PS};

        public static bool ContainsSpecialBreaks(this string target)
        {
            // <pex>
            if (target == null)
                throw new ArgumentNullException("target");
            // </pex>
            return target.IndexOfAny(SpecialBreaks) != -1;
        }

        public static bool Contains(this char[] me, char value)
        {
            // <pex>
            if (me == null)
                throw new ArgumentNullException("me");
            // </pex>
            for (int i = 0; i < me.Length; i++)
                if (me[i] == value) return true;
            return false;
        }
    }
}